package com.lry.util.geo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;

/**
 * 坐标工具
 *
 * @author liuxp
 * @author ALI
 */
public class GeoUtil {

    static final Pattern PATTERN = Pattern.compile("\\[|\\]");

    /**
     * 经纬度是否在区域内
     *
     * @param lon             经度
     * @param lat             纬度
     * @param areaPointString 区域坐标字符串，该字符串要求是每一个坐标点的集合，英文逗号分隔（"[String,String],[String,String]"）
     */
    public static boolean inArea(double lon, double lat, String areaPointString) {
        Coordinate checkPoint = new Coordinate(lon, lat);

        Matcher m = PATTERN.matcher(areaPointString);
        String[] points = m.replaceAll("").split(",");
        Coordinate[] pointList = new Coordinate[points.length / 2];
        for (int i = 0, j = 0; i < points.length; i += 2, j++) {
            pointList[j] = new Coordinate(Double.parseDouble(points[i]), Double.parseDouble(points[i + 1]));
        }

        return isInPolygon(checkPoint, pointList);
    }

    /**
     * 经纬度是否在区域内
     *
     * @param lon  经度
     * @param lat  纬度
     * @param area JSONArray是一组坐标面积，[[BigDecimal,BigDecimal],[BigDecimal,BigDecimal]]
     */
    public static boolean inArea(double lon, double lat, JSONArray area) {
        Coordinate checkPoint = new Coordinate(lon, lat);

        Coordinate[] pointList = new Coordinate[area.size()];
        for (int i = 0; i < area.size(); i++) {
            JSONArray j = (JSONArray) area.get(i);
            pointList[i] = new Coordinate(((BigDecimal) j.get(0)).doubleValue(), ((BigDecimal) j.get(1)).doubleValue());
        }
        return isInPolygon(checkPoint, pointList);
    }

    /**
     * 经纬度是否在区域内
     *
     * @param lon  经度
     * @param lat  纬度
     * @param area 多个坐标面积，JSONArray是一组坐标面积（[[BigDecimal,BigDecimal],[BigDecimal,BigDecimal]]）
     */
    public static boolean inArea(double lon, double lat, List<JSONArray> area) {
        for (JSONArray array : area) {
            boolean b = inArea(lon, lat, array);
            if (b) {
                return b;
            }
        }
        return false;
    }


    /**
     * 判断点是否在一个多边形中
     *
     * @param checkPoint 坐标
     * @param polygon    多边形坐标集合
     */
    public static boolean isInPolygon(Coordinate checkPoint, Coordinate[] polygon) {
        final GeometryFactory gf = new GeometryFactory();
        if (polygon.length == 2) {
            return checkPoint.x >= polygon[0].x && checkPoint.x <= polygon[1].x && checkPoint.y >= polygon[0].y
                && checkPoint.y <= polygon[1].y;
        }
        final Polygon poly = gf.createPolygon(
            new LinearRing(new CoordinateArraySequence(polygon), gf), null);

        final Coordinate coordinate = new Coordinate(checkPoint.x, checkPoint.y);
        final com.vividsolutions.jts.geom.Point p = gf.createPoint(coordinate);

        return p.within(poly);
    }

    /**
     * geojson 坐标数据转JSON
     *
     * @param geoCoordinateString geojson
     */
    public static List<JSONArray> geoCoordinateToJSONArray(String geoCoordinateString) {
        JSONArray array = JSON.parseArray(geoCoordinateString);
        if (array.size() > 1) {
            // 多面积
            List<JSONArray> result = new ArrayList<>();
            for (Object a : array) {
                JSONArray tempA = (JSONArray) a;
                // 这里就是一组面积
                result.add((JSONArray) tempA.get(0));
            }
            return result;
        } else {
            JSONArray single = (JSONArray) array.get(0);
            return Collections.singletonList((JSONArray) single.get(0));
        }
    }

    /**
     * 字符串的坐标转换为多组坐标
     *
     * @param cs json格式的坐标数据
     * @return list<坐标组>
     */
    public static List<List<Double[]>> getCoordinates(String cs) {
        JSONArray array = null;
        try {
            array = JSONArray.parseArray(cs);
        } catch (Exception e) {
            throw new RuntimeException("非坐标格式");
        }
        List<List<Double[]>> result = new ArrayList<>();
        for (Object a : array) {
            List<List<Double[]>> coordinatesFormObj = getCoordinatesFormObj(a);
            List<List<Double[]>> collect = coordinatesFormObj.stream().filter(CollectionUtils::isNotEmpty)
                                                             .collect(Collectors.toList());
            result.addAll(collect);

        }
        return result;
    }

    /**
     * json对象的坐标对象，解析为list<坐标组>格式
     */
    public static List<List<Double[]>> getCoordinates(JSONArray array) {
        List<List<Double[]>> result = new ArrayList<>();
        for (Object a : array) {
            List<List<Double[]>> coordinatesFormObj = getCoordinatesFormObj(a);
            List<List<Double[]>> collect = coordinatesFormObj.stream().filter(CollectionUtils::isNotEmpty)
                                                             .collect(Collectors.toList());
            result.addAll(collect);
        }
        return result;
    }

    /**
     * 判别坐标数据对象，并将器解析为list<坐标组>格式
     *
     * @return []：表示空数据，及当前传入的是一个坐标
     */
    private static List<List<Double[]>> getCoordinatesFormObj(Object obj) {
        List<List<Double[]>> result = new ArrayList<>();
        if (obj instanceof JSONArray) {
            // 点 或线面
            JSONArray jsonArray = (JSONArray) obj;
            if (jsonArray.get(0) instanceof JSONArray) {
                // 线、面等
                if (((JSONArray) jsonArray.get(0)).get(0) instanceof JSONArray) {
                    result.addAll(getCoordinatesFormObj(jsonArray.get(0)));
                } else {
                    // 一组组表
                    List<Double[]> temp = new ArrayList<>();
                    for (Object o : jsonArray) {
                        JSONArray to = (JSONArray) o;
                        temp.add(new Double[] {((BigDecimal) to.get(0)).doubleValue(),
                            ((BigDecimal) to.get(1)).doubleValue()});
                    }
                    result.add(temp);
                }
            } else {
                // 点
                return result;
            }
        }
        return result;
    }

}
